#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>

MODULE_LICENSE("Dual BSD/GPL");

struct proc_dir_entry *test_entry = NULL;
struct proc_dir_entry *wproc = NULL;
char dir_name[] = "proc_test_dir";
char file_name[] = "proc_test_file";

static int test_proc_write(struct file *file, const char __user *buffer,
		unsigned long count, void *data)
{
	char buf[10] = {0};

	if (count <= 1)
		return -EBADMSG;
	if (count > sizeof(buf) -1)
		return -EFBIG;
	if (copy_from_user(buf, buffer, count))
		return -EFAULT;
	printk(KERN_ERR "%s\n",  buf);

	return count;
}

static int test_proc_read(char *buf, char **start, off_t offset, int count,
		int *eof, void *data)
{
	*eof = 1;
	return sprintf(buf, "Hello World: proc test !\n");
}

static void destroy_test_proc(void)
{
	if (wproc != NULL){
		remove_proc_entry(file_name, test_entry);
		wproc = NULL;
	}
	if (test_entry != NULL){
		remove_proc_entry(dir_name, NULL);
		test_entry = NULL;
	}
}

static int create_test_rw_proc(struct proc_dir_entry *entry, char *file,
		void *wfunc, void *rfunc, void *data)
{
	wproc = create_proc_entry(file, 0644, entry);
	if (!wproc) 
		return -1;
	wproc->data = data;
	wproc->read_proc = rfunc;
	wproc->write_proc = wfunc;
	wproc->owner = THIS_MODULE;

	return 0;
}

static int create_test_proc(void)
{
	int ret = 0;

	test_entry = proc_mkdir(dir_name, NULL);
	if (!test_entry) {
		printk(KERN_ERR "proc_mkdir failed \n");
		return -1;
	}

	ret = create_test_rw_proc(test_entry, file_name, test_proc_write,
		test_proc_read, NULL);
	if (ret < 0) {
		remove_proc_entry(dir_name, NULL);
		test_entry = NULL;
	}

	return ret;
}

static __exit void test_exit(void)
{
	destroy_test_proc();
	printk(KERN_ERR "test exit \n");
}

static __init int test_init(void)
{
	int ret = 0;

	printk(KERN_ERR "test init \n");
	ret = create_test_proc();
	if (ret < 0)
		test_exit();
	return ret;
}

module_init(test_init);
module_exit(test_exit);

